package com.coalmine.api.controller;


import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.coalmine.api.annotation.LoginRequired;
import com.coalmine.api.domain.ApiConfig;
import com.coalmine.api.domain.ApiToken;
import com.coalmine.api.domain.req.ChangePasswordReqVO;
import com.coalmine.api.domain.req.TokenAuthReqVO;
import com.coalmine.api.domain.req.UserLoginReqVO;
import com.coalmine.api.mapper.ApiConfigMapper;
import com.coalmine.api.service.IApiApplyService;
import com.coalmine.api.service.IApiTokenService;
import com.coalmine.api.util.UUIDUtil;
import com.coalmine.common.annotation.Log;
import com.coalmine.common.constant.CacheConstants;
import com.coalmine.common.constant.CacheTimeConstants;
import com.coalmine.common.constant.HttpStatus;
import com.coalmine.common.core.controller.BaseController;
import com.coalmine.common.core.domain.AjaxResult;
import com.coalmine.common.core.page.TableDataInfo;
import com.coalmine.common.core.redis.RedisCache;
import com.coalmine.common.enums.BusinessType;
import com.coalmine.common.utils.StringUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * api的token信息表 前端控制器
 * </p>
 *
 * @author 尚郑
 * @since 2022-04-01
 */
@Slf4j
@Api(tags = "API-Token管理接口")
@RestController
@RequestMapping("/api/token")
public class ApiTokenController extends BaseController {

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private IApiTokenService tokenService;

    @Autowired
    IApiApplyService apiApplyService;

    @Autowired
    ApiConfigMapper configMapper;

    @PostMapping("/add")
    @ApiOperation("创建token")
    @PreAuthorize("@ss.hasPermi('api:token:add')")
    @Log(title = "API权限管理", businessType = BusinessType.INSERT)
    public AjaxResult add(ApiToken token) {
        ApiToken query = tokenService.getToken(token.getName(), token.getPassword());
        if (!ObjectUtil.isNull(query)) {
            return AjaxResult.error("用户名已存在！");
        }
        if (StringUtils.isBlank(token.getToken())) {
            String tokenStr = DigestUtils.md5Hex(UUID.randomUUID().toString());
            token.setToken(tokenStr);
        }
        token.setId(UUIDUtil.getUUID());
        token.setCreateBy(getUsername());
        token.setCreateTime(new Date());
        token.setUpdateBy(getUsername());
        token.setUpdateTime(new Date());
        return toAjax(tokenService.insert(token));
    }


    @GetMapping("/getAll")
    @ApiOperation("获取token列表")
    @PreAuthorize("@ss.hasPermi('api:token:list')")
    public TableDataInfo getAll() {
        startPage();
        List<ApiToken> list = tokenService.getAll();
        return getDataTable(list);
    }

    @DeleteMapping("/delete/{id}")
    @ApiOperation("删除token")
    @PreAuthorize("@ss.hasPermi('api:token:remove')")
    @Log(title = "API权限管理", businessType = BusinessType.DELETE)
    public AjaxResult delete(@PathVariable String id) {
        return toAjax(tokenService.deleteById(id));
    }

    @GetMapping("/generate")
    @ApiOperation("生成随机token")
    public String generate() {
        String token = DigestUtils.md5Hex(UUID.randomUUID().toString());
        return token;
    }

    /**
     * 用户名密码获取token
     * @param name
     * @param password
     * @return
     */
    @RequestMapping("/getToken")
    @ApiOperation("用户名密码获取token")
    public Map<String, Object> getToken(@RequestParam(value = "name",required = true) String name,
                                        @RequestParam(value = "password",required = true)String password){
        if (StrUtil.isBlank(name) && StrUtil.isBlank(password)){
            return AjaxResult.error(HttpStatus.BAD_REQUEST,"参数有误");
        }
        String token = tokenService.getTokenByUser(name, password);
        if (StrUtil.isNotBlank(token)){
            Map map = new HashMap();
            map.put("code", HttpStatus.SUCCESS);
            map.put("token", token);
            return map;
        }else {
            return AjaxResult.error("用户名密码错误！");
        }

    }
    @PostMapping("/auth")
    @ApiOperation("token授权api组")
    @PreAuthorize("@ss.hasPermi('api:token:edit')")
    @Log(title = "API权限管理", businessType = BusinessType.GRANT)
    public AjaxResult auth(@RequestBody TokenAuthReqVO reqVO) {
        boolean authed = tokenService.auth(reqVO.getTokenId(), reqVO.getGroupIds(), getUsername());
        if (!authed) {
            return AjaxResult.error("授权失败");
        }

         /*List<ApiApprove> authedApproveList = apiApplyService.listByTokenApi(reqVO.getTokenId(), Constants.AUTH_TOKEN_GROUP_API_FLAG);
        List<String> authedIds = authedApproveList
                .stream()
                .map(ApiApprove::getId)
                .collect(Collectors.toList());

       List<ApiApprove> approveList = new ArrayList<>();
        reqVO.getGroupIds().forEach(groupId -> {
            ApiApprove apiApprove = new ApiApprove();
            apiApprove.setId(UUIDUtil.getUUID());
            apiApprove.setApplyTime(new Date());
            apiApprove.setApproveType(EApproveStatus.APPROVED.getCode());
            apiApprove.setApiId(Constants.AUTH_TOKEN_GROUP_API_FLAG);
            apiApprove.setGroupId(groupId);
            apiApprove.setTokenId(reqVO.getTokenId());
            apiApprove.setApproveTime(new Date());
            apiApprove.setApproveReason("Token授权接口");
            apiApprove.setApplyReason("Token授权接口");

            approveList.add(apiApprove);
        });
        boolean success = apiApplyService.saveBatch(approveList);
        if (!success) {
            return AjaxResult.error("Token授权接口失败");
        }

        apiApplyService.removeByIds(authedIds);*/
        return AjaxResult.success();
    }

    @GetMapping("/getAuthGroups/{tokenId}")
    @ApiOperation("tokenId获取授权组")
    @PreAuthorize("@ss.hasPermi('api:token:query')")
    public List<String> getAuthGroups(@PathVariable("tokenId") String tokenId) {
        List<String> list = tokenService.getAuthGroups(tokenId);
        return list;
    }


    @RequestMapping("/checkToken")
    @ApiOperation("校验是否授权该资源")
    public AjaxResult checkToken(@RequestParam(value = "api",required = true) String api,
                                 @RequestParam(value = "token",required = true) String token){
        if (StrUtil.isBlank(api) && StrUtil.isBlank(token)){
            return AjaxResult.error(HttpStatus.BAD_REQUEST,"参数有误");
        }
        // 校验Token是否存在
        ApiToken apiToken = tokenService.getToken(token);
        if (ObjectUtil.isNull(apiToken)){
            return AjaxResult.error("token无效或者已过期");
        }
        // 校验接口资源是否存在
        ApiConfig apiConfig = configMapper.selectByPathOnline(api);
        if (ObjectUtil.isNull(apiConfig)){
            return AjaxResult.error("未找到该上线接口");
        }
        return tokenService.checkToken(api,apiToken.getId()) ? AjaxResult.success("已授权该资源"): AjaxResult.error("未授权该资源");
    }

    @PostMapping("/login")
    @ApiOperation("登录")
    AjaxResult login(@Valid @RequestBody UserLoginReqVO reqVO) {
        ApiToken token = tokenService.getToken(reqVO.getUsername(), reqVO.getPassword());
        if (ObjectUtil.isNull(token)) {
            // TODO P2: 限制错误登录次数
            return AjaxResult.error("用户名密码不正确！");
        }
//        boolean expired = tokenService.checkTokenExpire(token.getId());
//        if (expired) {
//            return AjaxResult.error("token已过期，请联系管理员！");
//        }
        // token缓存时间默认2小时
        final int tokenCacheTime = CacheTimeConstants.TIME_2H;
        final String tokenId = token.getId();
        String cacheKey = CacheConstants.USER_TOKEN + ":" + tokenId;
        redisCache.setCacheObject(cacheKey, token.getName(), tokenCacheTime, TimeUnit.SECONDS);
        return AjaxResult.success(tokenId);
    }

    @PostMapping("/logout")
    @ApiOperation("登出")
    @LoginRequired(required = true)
    AjaxResult logout(@RequestParam String username) {
        delTokenByName(username);
        return AjaxResult.success();
    }

    @PostMapping("/changePassword")
    @ApiOperation("密码修改")
    @LoginRequired(required = true)
    AjaxResult changePassword(@Valid @RequestBody ChangePasswordReqVO reqVO) {
        ApiToken token = tokenService.getToken(reqVO.getUsername(), reqVO.getOldPassword());
        if (ObjectUtil.isNull(token)) {
            return AjaxResult.error("用户名密码不正确！");
        }
        if (StringUtils.equals(reqVO.getOldPassword(), reqVO.getNewPassword())) {
            return AjaxResult.error("新旧密码相同！");
        }
        String oldPassword = DigestUtils.md5Hex(reqVO.getOldPassword());
        String newPassword = DigestUtils.md5Hex(reqVO.getNewPassword());
        int ret = tokenService.changePassword(reqVO.getUsername(), oldPassword, newPassword);
        if (ret <= 0) {
            return AjaxResult.error("修改失败，请重试！");
        }
        delTokenByName(token.getName());
        redisCache.deleteObject(com.coalmine.common.constant.Constants.API_TOKEN_KEY + token.getToken());
        return AjaxResult.success();
    }

    private void delTokenByName(String name) {
        String cacheKeyPattern = CacheConstants.USER_TOKEN + "*";
        Collection<String> keyList = redisCache.keys(cacheKeyPattern);
        for (String cacheKey: keyList) {
            String value = redisCache.getCacheObject(cacheKey);
            if (name.equals(value)) {
                redisCache.deleteObject(cacheKey);
            }
        }
    }
}
